import { ContextWithMiddleware } from './ContextWithMiddleware';
import type { TypedKey } from './types';

export type StorageType = 'localStorage' | 'sessionStorage';

export interface PersistentContextOptions {
  storageKey?: string;
  storageType?: StorageType;
  storagePrefix?: string;
  delay?: number;
  excludeKeys?: string[];
}

export class PersistentContext extends ContextWithMiddleware {
  private storageKey: string;
  private storageType: StorageType;
  private storagePrefix: string;
  private delay: number;
  private excludeKeys: string[];
  private saveTimeout: number | null = null;
  private storage: Storage | null = null;

  constructor(options: PersistentContextOptions = {}) {
    const {
      storageKey = 'app-state',
      storageType = 'localStorage',
      storagePrefix = 'typed-context-',
      delay = 0,
      excludeKeys = [],
    } = options;

    // 计算完整的存储键
    const fullStorageKey = `${storagePrefix}${storageKey}`;

    // 初始化存储
    let storage: Storage | null = null;
    if (typeof window !== 'undefined') {
      storage =
        storageType === 'localStorage'
          ? window.localStorage
          : window.sessionStorage;
    }

    // 加载初始数据
    super(PersistentContext.loadFromStorage(fullStorageKey, storage));

    // 初始化实例属性
    this.storageKey = storageKey;
    this.storageType = storageType;
    this.storagePrefix = storagePrefix;
    this.delay = delay;
    this.excludeKeys = excludeKeys;
    this.storage = storage;
  }

  private getFullStorageKey(): string {
    return `${this.storagePrefix}${this.storageKey}`;
  }

  private static loadFromStorage(
    storageKey: string,
    storage: Storage | null,
  ): Record<string, unknown> {
    try {
      if (!storage) return {};

      const stored = storage.getItem(storageKey);
      return stored ? JSON.parse(stored) : {};
    } catch (error) {
      console.warn(`Failed to load from ${storageKey}:`, error);
      return {};
    }
  }

  setData<T>(key: TypedKey<T>, value: T, immediate = true): void {
    super.setData(key, value, immediate);
    this.scheduleSave();
  }

  private scheduleSave(): void {
    // 清除之前的延迟保存
    if (this.saveTimeout) {
      clearTimeout(this.saveTimeout);
      this.saveTimeout = null;
    }

    if (this.delay <= 0) {
      // 立即保存
      this.saveToStorage();
    } else {
      // 延迟保存
      this.saveTimeout = setTimeout(() => {
        this.saveToStorage();
        this.saveTimeout = null;
      }, this.delay);
    }
  }

  private saveToStorage(): void {
    try {
      if (!this.storage) return;

      // 过滤掉不需要持久化的键
      const dataToSave = Object.entries(this.data)
        .filter(([key]) => !this.excludeKeys.includes(key))
        .reduce(
          (acc, [key, value]) => {
            acc[key] = value;
            return acc;
          },
          {} as Record<string, unknown>,
        );

      this.storage.setItem(
        this.getFullStorageKey(),
        JSON.stringify(dataToSave),
      );
    } catch (error) {
      console.warn(`Failed to save to ${this.storageType}:`, error);
    }
  }

  clearStorage(): void {
    try {
      if (!this.storage) return;

      this.storage.removeItem(this.getFullStorageKey());
      this.data = {};
      this.saveToStorage();
    } catch (error) {
      console.warn(`Failed to clear ${this.storageType}:`, error);
    }
  }

  // 手动保存到存储
  save(): void {
    this.saveToStorage();
  }

  // 重新加载从存储
  reload(): void {
    const data = PersistentContext.loadFromStorage(
      this.getFullStorageKey(),
      this.storage,
    );
    this.data = { ...data };
    // 通知所有订阅者
    this.notifyListeners('*', this.data);
  }

  // 获取当前存储类型
  getStorageType(): StorageType {
    return this.storageType;
  }

  // 更新存储类型
  setStorageType(type: StorageType): void {
    if (this.storageType !== type) {
      // 保存当前数据到新的存储类型
      const currentData = { ...this.data };
      this.storageType = type;
      this.storage =
        typeof window !== 'undefined'
          ? type === 'localStorage'
            ? window.localStorage
            : window.sessionStorage
          : null;
      this.data = currentData;
      this.saveToStorage();
    }
  }
}
